﻿using Application.Commons.Data;
using FluentValidation;
using FrameworkCore.Caches;
using Microsoft.Extensions.Configuration;
using FrameworkCore.Constants;
using FrameworkCore.DI;
using FrameworkCore.Events;
using FrameworkCore.Modules;
using FrameworkCore.Modules.Engines;
using FrameworkCore.Utilities;
using Infrastructure.Caches;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;
using System.Net;
using FrameworkCore.Converters;

namespace FrameworkCore.Extensions;

#nullable disable

/// <summary>
/// 表示对 IServiceCollection 的扩展
/// </summary>
public static class ServiceCollectionExtensions
{
    /// <summary>
    /// 配置基本应用程序设置
    /// </summary>
    /// <param name="services">服务描述符集合</param>
    /// <param name="builder">用于 Web 应用程序和服务的构建器</param>
    public static void ConfigureApplicationSettings(this IServiceCollection services,
        WebApplicationBuilder builder)
    {
        // 让操作系统决定使用哪个 TLS 协议版本
        // 参见 https://docs.microsoft.com/dotnet/framework/network-programming/tls
        ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault;

        // 创建默认文件提供程序
        CommonHelper.DefaultFileProvider = new AiBLSmartEduFileProvider(builder.Environment);

        // 注册类型查找器
        var typeFinder = new WebAppTypeFinder();
        Singleton<ITypeFinder>.Instance = typeFinder;
        services.AddSingleton<ITypeFinder>(typeFinder);
    }

    /// <summary>
    /// 将服务添加到应用程序并配置服务提供程序
    /// </summary>
    /// <param name="services">服务描述符集合</param>
    /// <param name="builder">用于 Web 应用程序和服务的构建器</param>
    public static void ConfigureApplicationServices(this IServiceCollection services,
        WebApplicationBuilder builder)
    {
        // 添加 HttpContext 访问器
        services.AddHttpContextAccessor();

        // 即时铜须
        services.AddSignalR();

        // 初始模块
        var mvcBuilder = services.AddMvcCore();
        mvcBuilder.PartManager.InitializeModules();

        //自动注册服务
        services.AddAutoDependencyInjectionServices();

        //添加配置端口号
        services.AddHostsUrls(builder);

        // 创建引擎并配置服务提供程序
        var engine = EngineContext.Create();

        // builder.Services.AddRateLimiter();//限制访问

        engine.ConfigureServices(services, builder.Configuration);
    }

    /// <summary>
    /// 注册 HttpContextAccessor
    /// </summary>
    /// <param name="services">服务描述符集合</param>
    public static void AddHttpContextAccessor(this IServiceCollection services)
    {
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    }

    /// <summary>
    /// 自动注册服务
    /// </summary>
    /// <param name="services">服务描述符集合</param>
    public static void AddAutoDependencyInjectionServices(this IServiceCollection services)
    {
        services.AddDependencyInjectionServices();
    }

    /// <summary>
    /// 添加和配置 MVC 到应用程序
    /// </summary>
    /// <param name="services">服务描述符集合</param>
    /// <returns>用于配置 MVC 服务的构建器</returns>
    public static IMvcBuilder AddAiBLSmartEduMvc(this IServiceCollection services)
    {
        // 添加基本的 MVC 功能
        var mvcBuilder = services.AddControllers().AddJsonOptions(options =>
        {
            options.JsonSerializerOptions.Converters.Add(new BigIntegerConverter());//解决long类型数据精度丢失问题
        });

        // 从 AiBLSmartEdu 程序集中注册所有可用的验证器
        var assemblies = mvcBuilder.PartManager.ApplicationParts
            .OfType<AssemblyPart>()
            .Where(part => part.Name.StartsWith(GlobalConstants.ModulePrefix, StringComparison.InvariantCultureIgnoreCase))
            .Select(part => part.Assembly);
        services.AddValidatorsFromAssemblies(assemblies);

        // 将控制器注册为服务，这样可以对其进行重写
        mvcBuilder.AddControllersAsServices();

        return mvcBuilder;
    }

    /// <summary>
    /// 添加配置端口号
    /// </summary>
    /// <param name="services"></param>
    /// <param name="builder"></param>
    /// <returns></returns>
    public static void AddHostsUrls(this IServiceCollection services, WebApplicationBuilder builder)
    {
        #region 端口配置
        var hosts = ConfigurationsExtensions.GetGlobalSection<string>(GlobalConstants.HostsKey) ?? "http://localhost:5000";
        builder.WebHost.UseUrls(hosts);
        #endregion
    }

    /// <summary>
    /// 添加缓存
    /// </summary>
    /// <param name="services"></param>
    /// <param name="config"></param>
    /// <returns></returns>
    public static IServiceCollection AddCacheService(this IServiceCollection services, IConfiguration config)
    {
        #region 缓存配置
        var cacheSettings = config.GetSection(CacheSettings.Key).Get<CacheSettings>()!;
        if (cacheSettings.CacheType.Equals(GlobalConstants.CacheTypeValue, StringComparison.OrdinalIgnoreCase))
        {
            // Redis/Garnet
            services.AddSingleton<IConnectionMultiplexer>(provider =>
            {
                var configuration = ConfigurationOptions.Parse(cacheSettings.RedisConnectionString);
                return ConnectionMultiplexer.Connect(configuration);
            });
            services.AddSingleton<ICacheService, RedisCacheService>();
        }
        else
        {
            // MemoryCache
            services.AddMemoryCache();
            services.AddSingleton<ICacheService, MemoryCacheService>();
        }

        return services;
        #endregion
    }

    /// <summary>
    /// 添加事件消费者服务
    /// </summary>
    /// <param name="services"></param>
    /// <param name="config"></param>
    /// <returns></returns>
    public static IServiceCollection AddConsumerService(this IServiceCollection services, IConfiguration config)
    {
        #region 事件消费者
        var typeFinder = Singleton<ITypeFinder>.Instance;
        var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();
        foreach (var consumer in consumers)
            foreach (var findInterface in consumer.FindInterfaces((type, criteria) =>
            {
                var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
                return isMatch;
            }, typeof(IConsumer<>)))
                services.AddScoped(findInterface, consumer);

        return services;
        #endregion
    }

    /// <summary>
    /// 添加数据库上下文服务
    /// </summary>
    /// <param name="services"></param>
    /// <param name="config"></param>
    /// <returns></returns>
    public static IServiceCollection AddDbContextService(this IServiceCollection services, IConfiguration config)
    {
        #region 数据库上下文
        string connectionString = config.GetConnectionString(GlobalConstants.DefaultConnectionKey);

        services.AddDbContext<EfCoreDbContext>(options => 
        { 
            options.UseSqlServer(connectionString);
        });
        services.AddScoped<EfCoreDbContext>();
        return services;
        #endregion
    }
}